package com.changgou.seckill.config;

import com.alibaba.fastjson.JSON;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/*通过该类对rabbitTemplate进行增强,通过该类完成消息的发送*/
@Component
public class ConfirmMessageSender implements RabbitTemplate.ConfirmCallback { //实现的接口,即是Confirm这个数据保护机制的回调接口

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisTemplate redisTemplate; //因为生产者在进行消息发送之前会把当前发送的消息保存到存储空间中,若消息服务器返回的是失败通知,那么消息生产者就会从存储空间中将该消息拿出进行重新的发送;若是成功消息,那么生产者会将存储空间的消息进行删除,对于存储空间的选型就是redis

    public static final String MESSAGE_CONFIRM_KEY="message_confirm_";

    public ConfirmMessageSender(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate; //进行声明
        rabbitTemplate.setConfirmCallback(this); //设置当前的回调函数,即消息同时返回之后需要返回给谁(即需要返回给本类,因为在本类中有confirm进行消息的接收)
    }


    /**
     * 即消息服务器向生产者来返回通知的时候,这个通知的返回会被当前的confirm方法来进行接收
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    //confirm的作用:接收消息服务器返回的通知的(相当于就是一个监听方法)
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack){
            //成功通知
            //删除redis中的相关数据
            redisTemplate.delete(correlationData.getId());
            redisTemplate.delete(MESSAGE_CONFIRM_KEY+correlationData.getId());
        }else{
            //失败通知
            //从redis中获取刚才的消息内容
            Map<String,String> map = (Map<String,String>)redisTemplate.opsForHash().entries(MESSAGE_CONFIRM_KEY+correlationData.getId());
            //重新发送
            String exchange = map.get("exchange");
            String routingkey = map.get("routingkey");
            String message = map.get("message");
            rabbitTemplate.convertAndSend(exchange,routingkey, JSON.toJSONString(message));
        }
    }

    //自定义消息发送方法
    public void sendMessage(String exchange,String routingKey,String message){

        //设置消息的唯一标识并存入到redis中(因为每一个消息都是不同的,因此每一个消息都要有一个唯一的标识,并且生产者还要知道这个唯一标识,后面才能进行消息的唯一获取)
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString()); //此时得到的uuid的值就是当前消息的唯一标识
        redisTemplate.opsForValue().set(correlationData.getId(),message);

        //将本次发送消息的相关元数据保存到redis中
        Map<String,String> map = new HashMap<>();
        map.put("exchange",exchange);
        map.put("routingKey",routingKey);
        map.put("message",message);
        redisTemplate.opsForHash().putAll(MESSAGE_CONFIRM_KEY+correlationData.getId(),map); //直接以map的形式进行数据的存放

        //携带着本次消息的唯一标识,进行数据发送
        rabbitTemplate.convertAndSend(exchange,routingKey,message,correlationData); //correlationData里面装的就是消息的唯一标识(进行消息返回的时候必须要指明是哪一个消息失败.,所以要有消息的唯一标识)

    }
}
